package com.gan.simple_init.util;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.util.IOUtils;
import com.gan.simple_init.common.constant.FileConstant;
import com.gan.simple_init.common.exception.BusinessException;
import com.gan.simple_init.common.exception.InvalidExtensionException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Paths;
import java.util.Objects;

/**
 * @author gbw
 * @date 2023/8/23
 */
@Component
@Slf4j
public class FileUtils {

    @Value("${files.baseDir}")
    private String defaultBaseDir;


    /**
     * 获取文件类型
     *
     * @param fileName
     * @return
     */
    public static String getFileType(String fileName) {
        int lastSeparatorIndex = fileName.lastIndexOf(".");
        if (lastSeparatorIndex == -1) {
            return "";
        }
        return fileName.substring(lastSeparatorIndex + 1).toLowerCase();
    }

    /**
     * 获取文件类型
     *
     * @param file
     * @return
     */
    public static String getFileType(File file) {
        if (file == null) {
            return "";
        }
        return getFileType(file.getName());
    }

    /**
     * 获取文件类型
     *
     * @param photoByte
     * @return
     */
    public static String getFileType(byte[] photoByte) {
        String strFileExtendName = "JPG";
        if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
                && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
            strFileExtendName = "GIF";
        } else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
            strFileExtendName = "JPG";
        } else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
            strFileExtendName = "BMP";
        } else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
            strFileExtendName = "PNG";
        }
        return strFileExtendName;
    }


    /**
     * 以默认配置进行文件上传,不指定文件后缀
     *
     * @param file 上传的文件
     * @return 文件名称
     * @throws Exception
     */
    public String upload(MultipartFile file) throws IOException
    {
        try
        {
            return upload(defaultBaseDir, file, FileConstant.DEFAULT_ALLOWED_EXTENSION);
        }
        catch (Exception e)
        {
            throw new IOException(e.getMessage(), e);
        }
    }

    /**
     * 根据文件路径上传,不指定文件后缀
     *
     * @param baseDir 相对应用的基目录
     * @param file 上传的文件
     * @return 文件名称
     * @throws IOException
     */
    public String upload(String baseDir, MultipartFile file) throws IOException
    {
        try
        {
            return upload(defaultBaseDir, file, FileConstant.DEFAULT_ALLOWED_EXTENSION);
        }
        catch (Exception e)
        {
            throw new IOException(e.getMessage(), e);
        }
    }


    /**
     * 默认文件路径上传,指定文件后缀
     *
     * @param allowedExtension 指定文件后缀
     * @param file 上传的文件
     * @return 文件名称
     * @throws IOException
     */
    public String upload(MultipartFile file, String[] allowedExtension) throws IOException
    {
        try
        {
            return upload(defaultBaseDir, file, allowedExtension);
        }
        catch (Exception e)
        {
            throw new IOException(e.getMessage(), e);
        }
    }

    /**
     * 指定文件路径上传,指定文件后缀
     *
     * @param baseDir          相对应用的基目录
     * @param file             上传的文件
     * @param allowedExtension 上传文件类型
     * @return 返回上传成功的文件名
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws IOException                    比如读写文件出错时
     */
    public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
            throws IOException {
        // 获得文件的全名称，即带后缀的名称
        int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length();
        // 文件名长度判断
        if (fileNameLength > FileConstant.DEFAULT_FILE_NAME_LENGTH) {
            throw new BusinessException("文件名称过长!");
        }
        // 文件名后缀判断
        assertAllowed(file, allowedExtension);
        // 获得由日期编码之后的文件名 2023/08/24/123_20230824104348A001.txt
        String fileName = extractFilename(file);
        // 获得完整路径
        String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
        // 将文件上传到完整路径上
        file.transferTo(Paths.get(absPath));
        return fileName;
    }


    /**
     * 从完全路径中获取文件名称 /profile/upload/2023/08/24/123.txt -- 123.txt
     *
     * @param fileName 路径名称
     * @return 没有文件路径的名称
     */
    public static String getName(String fileName)
    {
        if (fileName == null)
        {
            return null;
        }
        int lastUnixPos = fileName.lastIndexOf('/');
        int lastWindowsPos = fileName.lastIndexOf('\\');
        int index = Math.max(lastUnixPos, lastWindowsPos);
        return fileName.substring(index + 1);
    }


    /**
     * 编码文件名
     */
    public static final String extractFilename(MultipartFile file)
    {
        return String.format("%s/%s_%s.%s", DateUtil.datePath(),
                FileUtil.getPrefix(file.getOriginalFilename()),SeqUtil.getId(SeqUtil.uploadSeqType),FileUtil.getSuffix(file.getOriginalFilename()));
    }

    /**
     * 创造文件
     * @param uploadDir
     * @param fileName
     * @return
     * @throws IOException
     */
    public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
    {
        String filePath = uploadDir + File.separator + fileName;
        File desc = new File(filePath);

        if (!desc.exists())
        {
            if (!desc.getParentFile().exists())
            {
                desc.getParentFile().mkdirs();
            }
        }
        return desc;
    }


    /**
     * 文件大小校验&判断文件类型是否合法
     *
     * @param file 上传的文件
     * @return
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws InvalidExtensionException
     */
    public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
            throws InvalidExtensionException {
        long size = file.getSize();
        if (size > FileConstant.DEFAULT_MAX_SIZE) {
            throw new BusinessException("文件过大,请重新上传!");
        }
        String fileName = file.getOriginalFilename();
        // 使用huTool获取不带"."的文件后缀
        String extension = FileUtil.getSuffix(file.getOriginalFilename());
        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
            throw new InvalidExtensionException(allowedExtension, extension, fileName);
        }
    }

    /**
     * 判断后缀是否合法
     *
     * @param extension
     * @param allowedExtension
     * @return
     */
    public static boolean isAllowedExtension(String extension, String[] allowedExtension) {
        for (String str : allowedExtension) {
            if (str.equalsIgnoreCase(extension)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 检查文件是否可下载
     *
     * @param resource 需要下载的文件
     * @return true 正常 false 非法
     */
    public static boolean checkAllowDownload(String resource)
    {
        // 禁止目录上跳级别
        if (StringUtils.contains(resource, ".."))
        {
            return false;
        }

        // 检查允许下载的文件规则
        if (ArrayUtils.contains(FileConstant.DEFAULT_ALLOWED_EXTENSION, getFileType(resource)))
        {
            return true;
        }

        // 不在允许下载的文件规则
        return false;
    }

    /**
     * 下载文件名重新编码
     *
     * @param response 响应对象
     * @param realFileName 真实文件名
     * @return
     */
    public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException
    {
        response.setHeader("Content-Disposition", "attachment;filename="+ URLEncoder.encode(realFileName  ,"utf-8"));
    }


    /**
     * 输出指定文件的byte数组
     *
     * @param filePath 文件路径
     * @param os 输出流
     * @return
     */
    public static void writeBytes(String filePath, OutputStream os) throws IOException
    {
        FileInputStream fis = null;
        try
        {
            File file = new File(filePath);
            if (!file.exists())
            {
                throw new BusinessException("查询不到该文件");
            }
            fis = new FileInputStream(file);
            byte[] b = new byte[1024];
            int length;
            while ((length = fis.read(b)) > 0)
            {
                os.write(b, 0, length);
            }
        }
        catch (IOException e)
        {
            log.error("文件导出失败{}",e.getMessage());
            throw new BusinessException(e.getMessage());
        }
        finally
        {
            IOUtils.close(os);
            IOUtils.close(fis);
        }
    }

    /**
     * 删除文件
     *
     * @param filePath 文件
     * @return
     */
    public static boolean deleteFile(String filePath)
    {
        boolean flag = false;
        File file = new File(filePath);
        // 路径为文件且不为空则进行删除
        if (file.isFile() && file.exists())
        {
            flag = file.delete();
        }
        return flag;
    }


}
